﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DiscreteEventModel.Common;

namespace DiscreteEventModel {

    public struct DiscreteTime : IComparable<DiscreteTime>, IEquatable<DiscreteTime>, IBinarySerializable {

        private double _major;
        private double _error;

        public static DiscreteTime Base {
            get {
                return new DiscreteTime();
            }
        }

        public static DiscreteTime Infinity {
            get {
                return new DiscreteTime() {
                    _major = double.PositiveInfinity,
                    _error = double.NaN
                };
            }
        }

        public static DiscreteTime Null {
            get {
                return new DiscreteTime() {
                    _major = double.NaN,
                    _error = double.NaN
                };
            }
        }

        public bool IsInfinity {
            get {
                return double.IsPositiveInfinity(this._major);
            }
        }

        public bool IsNull {
            get {
                return double.IsNaN(this._major);
            }
        }

        public static DiscreteTime operator +(DiscreteTime time, double delta) {
            DiscreteTime newTime;
            double t = delta + time._error;
            newTime._major = time._major + t;
            newTime._error = t - (newTime._major - time._major);
            return newTime;
        }

        public static DiscreteTime operator -(DiscreteTime time, double delta) {
            return time + (-delta);
        }

        public static double operator -(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error);
        }

        public static bool operator ==(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) == 0d;
        }

        public static bool operator !=(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) != 0d;
        }

        public static bool operator >(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) > 0d;
        }

        public static bool operator >=(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) >= 0d;
        }

        public static bool operator <(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) < 0d;
        }

        public static bool operator <=(DiscreteTime time1, DiscreteTime time2) {
            return (time1._major - time2._major) + (time1._error - time2._error) <= 0d;
        }

        public int CompareTo(DiscreteTime other) {
            return ((this._major - other._major) + (this._error - other._error)).CompareTo(0d);
        }

        public bool Equals(DiscreteTime other) {
            return (this._major - other._major) + (this._error - other._error) == 0d;
        }

        public override bool Equals(object obj) {
            DiscreteTime other = (DiscreteTime)obj;
            return (this._major - other._major) + (this._error - other._error) == 0d;
        }

        public override int GetHashCode() {
            return (this._major + this._error).GetHashCode();
        }

        public static DiscreteTime Min(DiscreteTime time1, DiscreteTime time2) {
            return time1 < time2 ? time1 : time2;
        }

        public static DiscreteTime Max(DiscreteTime time1, DiscreteTime time2) {
            return time1 > time2 ? time1 : time2;
        }

        public void Serialize(BinaryWriter writer) {
            writer.Write(this._major);
            writer.Write(this._error);
        }

        public bool Deserialize(BinaryReader reader) {
            this._major = reader.ReadDouble();
            this._error = reader.ReadDouble();
            return true;
        }
    }
}
